
local BasicSerialyze = function(o)
  if type(o) == "string" then
    return string.format("%q", o)
  end
  return tostring(o)
end

local reserved_words = {}

local sep = ' '

local get_table_key = function(key)
end

--por enquanto todas as chaves devem estar entre []
local ExtractName = function(str,i)
  if str[i] == '{'   then i = i-1 end
  if str[i] == ' = ' then i = i-1 end
  if str[i] == '"]' or str[i] == ']' then
    local ret = str[i] == '"]' and str[i-1] or tonumber(str[i-1])
    local close_count = 0
    while str[i] ~= ',' and str[i] ~= '{' and i > 1 do
      i=i-1
    end
    return ret,i
  --temporario, funciona para o primeiro nome
  elseif i == 1 then
    return str[1],i
  end
end

--deve ir percorrendo a tabela str coletando os nomes das chave até
--coletar o nome da tabela, se a tabela for anonima (estiver precedida de um return)
--gerea um erro
local GetTableName = function(str)
  local name,i = ExtractName(str, #str - 1)
  local closed = 0
  local temp
  while i >= 1 do
    if str[i] == '}' then
      closed = closed + 1
    elseif str[i] == '{' then
      if closed == 0 then
        temp,i = ExtractName(str,i)
        --name = name .. '.' .. temp
        name = temp .. '.' .. name
      else
        closed = closed - 1
      end
    end
    i=i-1
  end
  return name
end

--deve remover ate a primeira ',' ou até a primeira '{'
local RemovePreviousKey = function(str)
  while str[#str] ~= ',' and str[#str] ~= '{' do
    table.remove(str)
  end
  table.insert(str,sep)
end

--depois deixa-la mais sofisticada`
local InsertKey = function(str, ident, key)
  local brac_op = type(key)=='number' and '[' or '["'
  local brac_cl = type(key)=='number' and ']' or '"]'
  table.insert(str,ident)
  table.insert(str,brac_op)
  table.insert(str,tostring(key))
  table.insert(str,brac_cl)
  table.insert(str,' = ')
end

-- o retorno indica se foi encontrado ciclo
local function SerialyzeAux(str, value, extern_names, local_tables, ident)
  if extern_names[value] then
    table.insert(str, extern_names[value])
  elseif type(value) ~= 'table' then
    table.insert(str,BasicSerialyze(value))
  else
    if local_tables[value] then
      table.insert(local_tables[value], GetTableName(str))
      RemovePreviousKey(str)
      return true
    else
      --insere na tabela do value para ser adicionado depois
      local_tables[value] = {name = GetTableName(str)}
      local viewd_ind = {}
      local local_ident = ident .. '  '
      table.insert(str,'{')
      table.insert(str,sep)
      -- imprime [i] = v, 
      for i,v in ipairs(value) do
        viewd_ind[i] = true
        InsertKey(str,local_ident,i)
        local _c = SerialyzeAux(str, v, extern_names, local_tables, local_ident)
        -- se o valor tiver sido posto dentro da tabela colocar virgula
        if not _c then
          table.insert(str,',')
          table.insert(str,sep)
        end
      end
      for k,v in pairs(value) do
        if not viewd_ind[k] then
          InsertKey(str,local_ident,k)
          local cycle = SerialyzeAux(str, v, extern_names, local_tables, local_ident)
          if not cycle then
            table.insert(str,',')
            table.insert(str,sep)
          end
        end
      end
      table.insert(str, ident)
      table.insert(str, '}')
    end
  end
end

local function Serialyze(name, value, extern_names)
  extern_names = extern_names or {}
  local str = {}
  local local_tables = {}
  table.insert(str, name)
  table.insert(str, ' = ')
  SerialyzeAux(str, value, extern_names, local_tables, '')
  for tab_name,v in pairs(local_tables) do
    for i,name in ipairs(v) do
      table.insert(str, sep)
      table.insert(str,name)
      table.insert(str,' = ')
      table.insert(str, v.name)
    end
  end
  table.insert(str, sep)
  return table.concat(str), next(local_tables) and local_tables
end

--print(Serialyze('w',10))
--print(Serialyze('w',true))
--print(Serialyze('w','bernado'))
--print(Serialyze('w',{1,2,4}))
--print(Serialyze('w',{x=1,y=2,z=4}))
--[[print(Serialyze('w',{
  x=1,
  y=2,
  z={
    1,2,3,
    c = "aaaaa"
  }
}))]]
--[[
local test = {
  x = {1,2}
}
test.y = test.x
test.a = test
test.z = {t = test}
print(Serialyze('w',test))

local s = Serialyze('w',test)
assert(loadstring(s))()
]]

return Serialyze
